#include "Server.h"

#include "../util/definitions.h"

#include "../lib/SignalHandler.h"
#include "../lib/MessageQueueException.h"

#include <sys/msg.h>
#include <sys/ipc.h>
#include <iostream>
#include <unistd.h>
#include <fcntl.h>

Server::Server() {

	SignalHandler::getInstance().desbloquearSenial(SIGTERM);
	SignalHandler::getInstance().desbloquearSenial(SIGINT);
	SignalHandler::getInstance().registrarHandler(SIGTERM,&sigTerm);
	SignalHandler::getInstance().registrarHandler(SIGINT,&sigTerm);

	my_pid = -1;
	is_channel_open = false;
}

Server::~Server() {

}

void Server::openChannel() {
	if ( ! is_channel_open ) {

		/* abrir la cola de mensajes (crea el archivo necesario si no existe) */

		std::string msgFile (MESSAGE_QUEUE_F);
		int fq = open(msgFile.c_str(), O_CREAT | O_RDONLY, 0660);
		close(fq);

		channel.open(msgFile.c_str(), MESSAGE_QUEUE_L);
		is_channel_open = true;
	}
}

bool Server::init() {

	bool has_init = true;

	my_pid = getpid();

	std::cout<<"++ Iniciando Servidor... pid = "<<my_pid<<std::endl;

	/* cargar la base de datos */
	std::cout<<"++ Cargando Base de datos"<<std::endl;
	db.load();
	std::cout<<"++ Base de datos Cargada"<<std::endl;

	try{
		/* crear la cola de mensajes */
		openChannel();

	} catch ( MessageQueueException &e ) {

		std::clog<<"++ Error al crear la cola de mensajes"<<std::endl;
		has_init = false;
	}

	return has_init;
}

void Server::finalize() {

	/* destruir la cola de mensajes */
	remove(MESSAGE_QUEUE_F);

	channel.removeQueue();

	/* guardar y cerrar la base de datos */
	db.save();
	db.finalize();

	my_pid = -1;

	std::cout<<"++ Servidor Cerrado"<<std::endl;
}

int Server::run() {

	bool resource_ok = init();

	while (isRunning() && resource_ok) {
		Message req;

		try {

			waitForRequest(&req);
			processRequest(&req);

		} catch ( MessageQueueException &e ) {
			std::cout<<"++ Cola de mesajes cerrada"<<std::endl;
			resource_ok = false;
		}
	}

	finalize();

	return resource_ok;
}

void Server::waitForRequest(Message *req) {

	if (req != NULL) {

		/* esperar mensaje en la cola de msj con id de server */
		std::cout<<std::endl<<"++ Esperando request ++"<<std::endl<<std::endl;

		channel.receive(MTYPE_SERVER, req);

		std::cout<<"++ Request recibida"<<std::endl;
		req->print();
	}
}

void Server::processRequest(Message *req) {

	if ( req != NULL ) {

		switch ( req->cmd )	{
		case request_get:
			processGet(req);
			break;
		case request_insert:
			processInsert(req);
			break;
		case request_update:
			processUpdate(req);
			break;
		case request_remove:
			processRemove(req);
			break;
		default:
			processError(req);
			break;
		}
	}
}

void Server::processGet(Message* request) {

	if ( request != NULL ) {
		std::cout<<"++ Procesando Get"<<std::endl;

		/* obtener el dato desde la base de datos */

		const DBRegister *reg = db.getRegister(request->reg.name);

		/* armar el msj de respuesta */

		Message response;
		response.mtype 		= request->dst_mtype;	/* poner el mtype de destino del cliente */
		response.dst_mtype 	= MTYPE_SERVER;

		if ( reg !=  NULL ) {
			response.cmd	= result_ok;	/* indicar el resultado del get */
			response.reg 	= (*reg);		/* copiar el registro de la base de datos */
		}
		else {
			response.cmd	= result_error;	/* informar del error */
		}

		channel.send(&response);

		std::cout<<"++ Respuesta Get Enviada"<<std::endl;
		response.print();
	}
}

void Server::processInsert(Message* request) {

	if ( request != NULL ) {

		std::cout<<"++ Procesando Insert"<<std::endl;

		/* realizar el insert en la base de datos */

		int result = db.addRegister( &(request->reg) );

		/* armar el msj de respuesta */

		Message response;
		response.dst_mtype 	= MTYPE_SERVER;
		response.mtype		= request->dst_mtype;	/* poner el mtype de destino del cliente */
		response.cmd		= result;				/* indicar el resultado del insert */

		channel.send(&response);

		std::cout<<"++ Respuesta Insert Enviada"<<std::endl;
		response.print();
	}
}

void Server::processUpdate(Message *request) {

	if ( request != NULL ) {

		std::cout<<"++ Procesando Update"<<std::endl;

		/* realizar el update en la base de datos */

		int result = db.updateRegister( &(request->reg) );

		/* armar el msj de respuesta */

		Message response;
		response.dst_mtype 	= MTYPE_SERVER;
		response.mtype		= request->dst_mtype;	/* poner el mtype de destino del cliente */
		response.cmd		= result;				/* indicar el resultado del update */

		channel.send(&response);

		std::cout<<"++ Respuesta Update Enviada"<<std::endl;
		response.print();
	}
}

void Server::processRemove(Message *request) {

	if ( request != NULL ) {

		std::cout<<"++ Procesando Remove"<<std::endl;

		/* realizar el remove en la base de datos */
		int result = db.removeRegister( &(request->reg) );

		/* armar el msj de respuesta */
		Message response;
		response.dst_mtype	= MTYPE_SERVER;
		response.mtype		= request->dst_mtype;	/* poner el mtype de destino del cliente */
		response.cmd		= result;				/* indicar el resultado del update */

		channel.send(&response);

		std::cout<<"++ Respuesta Remove Enviada"<<std::endl;
		response.print();
	}
}

void Server::processError(Message *request) {

	if ( request != NULL ) {
		std::cout<<"++ Solicitud Inválida... Enviando error"<<std::endl;

		request->mtype 		= request->dst_mtype;
		request->dst_mtype 	= MTYPE_SERVER;
		request->cmd 		= result_invalid;

		channel.send(request);

		std::cout<<"++ Error Enviado: "<<std::endl;
		request->print();
	}
}


bool Server::isRunning() {
	return !sigTerm.getGracefulQuit();
}

void Server::stopServer() {
	if ( my_pid != -1 )
		SignalHandler::emitirSenial(my_pid, SIGTERM);
}


